package commons.base.util;

import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 反射工具类
 * @author yuan<cihang.yuan@happyelements.com>
 *
 */
public class ReflectUtil {

	public static interface BasicTypeParser<T>{
		public T parse(String raw);
	}
	
	private static MethodHandles.Lookup lookup = MethodHandles.lookup();
	
	private static Map<Class<?>, BasicTypeParser<?>> basicTypeParserMap = new HashMap<Class<?>, BasicTypeParser<?>>();
	
	static {
		BasicTypeParser<Boolean> booleanParser = new BasicTypeParser<Boolean>() {
			@Override
			public Boolean parse(String raw) {
				return Boolean.parseBoolean(raw);
			}
		};
		basicTypeParserMap.put(Boolean.class, booleanParser);
		basicTypeParserMap.put(Boolean.TYPE, booleanParser);
		
		BasicTypeParser<Character> characterParser = new BasicTypeParser<Character>() {
			@Override
			public Character parse(String raw) {
				return raw.trim().charAt(0);
			}
		};
		basicTypeParserMap.put(Character.class, characterParser);
		basicTypeParserMap.put(Character.TYPE, characterParser);
		
		BasicTypeParser<Byte> byteParser = new BasicTypeParser<Byte>() {
			@Override
			public Byte parse(String raw) {
				return Byte.parseByte(raw.trim());
			}
		};
		basicTypeParserMap.put(Byte.class, byteParser);
		basicTypeParserMap.put(Byte.TYPE, byteParser);
		
		BasicTypeParser<Short> shortParser = new BasicTypeParser<Short>() {
			@Override
			public Short parse(String raw) {
				return Short.parseShort(raw.trim());
			}
		};
		basicTypeParserMap.put(Short.class, shortParser);
		basicTypeParserMap.put(Short.TYPE, shortParser);
		
		BasicTypeParser<Integer> integerParser = new BasicTypeParser<Integer>() {
			@Override
			public Integer parse(String raw) {
				return Integer.parseInt(raw.trim());
			}
		};
		basicTypeParserMap.put(Integer.class, integerParser);
		basicTypeParserMap.put(Integer.TYPE, integerParser);
		
		BasicTypeParser<Long> longParser = new BasicTypeParser<Long>() {
			@Override
			public Long parse(String raw) {
				return Long.parseLong(raw.trim());
			}
		};
		basicTypeParserMap.put(Long.class, longParser);
		basicTypeParserMap.put(Long.TYPE, longParser);
		
		BasicTypeParser<Float> floatParser = new BasicTypeParser<Float>() {
			@Override
			public Float parse(String raw) {
				return Float.parseFloat(raw.trim());
			}
		};
		basicTypeParserMap.put(Float.class, floatParser);
		basicTypeParserMap.put(Float.TYPE, floatParser);
		
		BasicTypeParser<Double> doubleParser = new BasicTypeParser<Double>() {
			@Override
			public Double parse(String raw) {
				return Double.parseDouble(raw.trim());
			}
		};
		basicTypeParserMap.put(Double.class, doubleParser);
		basicTypeParserMap.put(Double.TYPE, doubleParser);
		
		basicTypeParserMap.put(BigInteger.class, new BasicTypeParser<BigInteger>() {
			@Override
			public BigInteger parse(String raw) {
				return new BigInteger(raw.trim());
			}
		});
		basicTypeParserMap.put(BigDecimal.class, new BasicTypeParser<BigDecimal>() {
			@Override
			public BigDecimal parse(String raw) {
				return new BigDecimal(raw.trim());
			}
		});
		basicTypeParserMap.put(String.class, new BasicTypeParser<String>() {
			@Override
			public String parse(String raw) {
				return raw;
			}
		});
		basicTypeParserMap.put(java.util.Date.class, new BasicTypeParser<java.util.Date>() {
			@Override
			public java.util.Date parse(String raw) {
				try {
					return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(raw.trim());
				} catch (ParseException e) {
					e.printStackTrace();
				}
				return null;
			}
		});
	}
	
	/**
	 * 是否基本数据类型
	 * @param clazz
	 * @return
	 */
	public static boolean isBasicType(Class<?> clazz) {
		return basicTypeParserMap.containsKey(clazz) || (clazz.isEnum());
	}
	
	/**
	 * 将字符串解析为基本类型对象
	 * @param raw
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Object parseBasicObject(String raw, Class<?> clazz){
		if(raw == null){
			return null;
		}
		
		if(basicTypeParserMap.containsKey(clazz)){
			return basicTypeParserMap.get(clazz).parse(raw);
		}else if(clazz.isEnum()){
			return Enum.valueOf((Class<? extends Enum>)clazz, raw.trim());
		}else{
			return raw;
		}
	}
	
	/**
	 * 获取字段的泛型类型
	 * @param f
	 * @param index
	 * @return
	 */
	public static Class<?> getParameterizedType(Field f, int index){
		return (Class<?>)((ParameterizedType)f.getGenericType()).getActualTypeArguments()[index];
	}
	
	/**
	 * 获取泛型类型列表
	 * @param type
	 * @return
	 */
	public static List<Class<?>> getGenericParameterTypes(Type type){
		List<Class<?>> list = new ArrayList<Class<?>>();
		if (type instanceof ParameterizedType) {    
		    ParameterizedType paramType = (ParameterizedType)type;    
		    Type[] actualTypes = paramType.getActualTypeArguments();    
		    for (Type aType : actualTypes) {        
		        if (aType instanceof Class) {        
		           Class<?> clazz = (Class<?>) aType;            
		           list.add(clazz);      
		        }else if(aType instanceof ParameterizedType){//多级泛型
		        	Class<?> clazz = (Class<?>)((ParameterizedType) aType).getRawType();            
			        list.add(clazz);
		        }else if(aType instanceof GenericArrayType){//数组泛型
		        	Class<?> clazz = (Class<?>)((GenericArrayType) aType).getGenericComponentType();            
			        list.add(Array.newInstance(clazz, 0).getClass());
		        }
		    }
		}  
		return list;
	}
	/**
	 * 获取泛型类型列表
	 * @param obj
	 * @return
	 */
	public static List<Class<?>> getGenericParameterTypes(Object obj){
		List<Class<?>> list = new ArrayList<Class<?>>();
		Type genType = obj.getClass().getGenericSuperclass();
		if (genType instanceof ParameterizedType) {    
			ParameterizedType paramType = (ParameterizedType)genType;    
			Type[] actualTypes = paramType.getActualTypeArguments();    
			for (Type aType : actualTypes) {        
				if (aType instanceof Class) {        
		           Class<?> clazz = (Class<?>) aType;            
		           list.add(clazz);      
		        }else if(aType instanceof ParameterizedType){//多级泛型
		        	Class<?> clazz = (Class<?>)((ParameterizedType) aType).getRawType();            
			        list.add(clazz);
		        }else if(aType instanceof GenericArrayType){//数组泛型
		        	Class<?> clazz = (Class<?>)((GenericArrayType) aType).getGenericComponentType();            
		        	list.add(Array.newInstance(clazz, 0).getClass());
		        }   
			}
		}  
		return list;
	}
	/**
	 * 获取字段的泛型类型列表
	 * @param field
	 * @return
	 */
	public static List<Class<?>> getGenericFieldType(Field field){
		return getGenericParameterTypes(field.getGenericType());
	}
	/**
	 * 获取方法的泛型类型列表
	 * @param method
	 * @return
	 */
	public static List<Class<?>> getGenericReturnType(Method method){
		return getGenericParameterTypes(method.getGenericReturnType());
	}
	/**
	 * 获取泛型类型列表
	 * @param method
	 * @param paramIndex
	 * @return
	 */
	public static List<Class<?>> getGenericParamType(Method method, Integer paramIndex){
		return getGenericParameterTypes(method.getGenericParameterTypes()[paramIndex]);
	}
	
	/**
	 * 获取被注解的第一个方法
	 * @param clazz
	 * @param annotationClass
	 * @return
	 * @throws IllegalAccessException
	 */
	public static MethodHandle getFirstMethodByAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) {
		Method[] ms = clazz.getDeclaredMethods();
		
		for(Method m : ms){
			if(m.isAnnotationPresent(annotationClass)){
				try {
					return lookup.unreflect(m);
				} catch (Exception e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
		}
		
		return null;
	}
	
	/**
	 * 获取被注解的方法列表
	 * @param clazz
	 * @param annotationClass
	 * @return
	 * @throws IllegalAccessException
	 */
	public static List<MethodHandle> getMethodsByAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) {
		List<MethodHandle> list = new ArrayList<MethodHandle>();
		
		Method[] ms = clazz.getDeclaredMethods();
		
		for(Method m : ms){
			if(m.isAnnotationPresent(annotationClass)){
				try {
					list.add(lookup.unreflect(m));
				} catch (Exception e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
		}
		
		return list;
	}
	
}
